import test from "ava"
import ceno from "../src"
import { createHttpTestServer } from "./helpers/create-http-test-server"

test('选项 - json 选项发送 JSON 数据', async (t) => {
    const server = await createHttpTestServer()

    server.post('/json', (request, response) => {
        t.is(request.headers['content-type'], 'application/json')
        response.json({ received: request.body })
    })

    const testData = { name: 'test', value: 123 }

    interface Result {
        received: {
            name: string
            value: number
        }
    }

    const data = await ceno.post<Result>(server.url + '/json', {
        json: testData
    }).json()

    t.deepEqual(data.received, testData)

    await server.close()
})

test('选项 - parseJson 自定义 JSON 解析器', async (t) => {
    const server = await createHttpTestServer()

    server.get('/custom-json', (request, response) => {
        response.type('application/json').send('{"value": 42}')
    })

    const data = await ceno.get(server.url + '/custom-json', {
        parseJson: (text: string) => {
            const parsed = JSON.parse(text)
            return { ...parsed, custom: true }
        }
    }).json()

    t.deepEqual(data, { value: 42, custom: true })

    await server.close()
})

test('选项 - stringifyJson 自定义 JSON 序列化器', async (t) => {
    const server = await createHttpTestServer()

    server.post('/stringify-json', (request, response) => {
        t.is(request.body.name, 'test')
        t.is(request.body.created_at, '2023-01-01') // 现在应该是正确的
        response.json({ success: true })
    })

    const testData = { name: 'test', created_at: new Date('2023-01-01') }

    interface Result {
        success: boolean
    }

    const data = await ceno.post<Result>(server.url + '/stringify-json', {
        json: testData,
        stringifyJson: (data) => JSON.stringify(data, (key, value) => {
            // 此时 value 已经是字符串形式的 ISO 日期
            if (key === 'created_at' && typeof value === 'string' && value.includes('T')) {
                return value.split('T')[0] // 从 ISO 字符串提取日期部分
            }
            return value
        })
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - searchParams 对象格式', async (t) => {
    const server = await createHttpTestServer()

    server.get('/search', (request, response) => {
        t.is(request.query.page, '1')
        t.is(request.query.limit, '10')
        t.is(request.query.active, 'true')
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/search', {
        searchParams: {
            page: 1,
            limit: 10,
            active: true
        }
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - searchParams 字符串格式', async (t) => {
    const server = await createHttpTestServer()

    server.get('/search', (request, response) => {
        t.is(request.query.foo, 'bar')
        t.is(request.query.baz, 'qux')
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/search', {
        searchParams: 'foo=bar&baz=qux'
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - searchParams URLSearchParams 格式', async (t) => {
    const server = await createHttpTestServer()

    server.get('/search', (request, response) => {
        t.is(request.query.name, 'ceno')
        t.is(request.query.version, '1.0')
        response.json({ success: true })
    })

    const params = new URLSearchParams()
    params.append('name', 'ceno')
    params.append('version', '1.0')

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/search', {
        searchParams: params
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - searchParams 数组格式', async (t) => {
    const server = await createHttpTestServer()

    server.get('/search', (request, response) => {
        // 当有重复参数时，Express会返回数组
        t.deepEqual(request.query.tags, ['tag1', 'tag2'])
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/search', {
        searchParams: [['tags', 'tag1'], ['tags', 'tag2']]
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - prefixUrl 基本使用', async (t) => {
    const server = await createHttpTestServer()

    server.get('/api/users', (request, response) => {
        response.json({ endpoint: '/api/users' })
    })

    interface Result {
        endpoint: string
    }

    const data = await ceno.get<Result>('users', {
        prefixUrl: server.url + '/api'
    }).json()

    t.deepEqual(data, { endpoint: '/api/users' })

    await server.close()
})

test('选项 - prefixUrl 自动添加斜杠', async (t) => {
    const server = await createHttpTestServer()

    server.get('/api/posts', (request, response) => {
        response.json({ endpoint: '/api/posts' })
    })

    interface Result {
        endpoint: string
    }

    const data = await ceno.get<Result>('posts', {
        prefixUrl: server.url + '/api' // 没有结尾斜杠
    }).json()

    t.deepEqual(data, { endpoint: '/api/posts' })

    await server.close()
})

test('选项 - timeout 超时设置', async (t) => {
    const server = await createHttpTestServer()

    server.get('/slow', (request, response) => {
        setTimeout(() => {
            response.json({ success: true })
        }, 1000)
    })

    interface Result {
        success: boolean
    }

    await t.throwsAsync(
        () => ceno.get(server.url + '/slow', { timeout: 500 }),
        { name: 'TimeoutError' }
    )

    await server.close()
})

test('选项 - timeout: false 禁用超时', async (t) => {
    const server = await createHttpTestServer()

    server.get('/slow', (request, response) => {
        setTimeout(() => {
            response.json({ success: true })
        }, 500)
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/slow', {
        timeout: false
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - throwHttpErrors: true 抛出错误', async (t) => {
    const server = await createHttpTestServer()

    server.get('/error', (request, response) => {
        response.status(404).json({ error: 'Not Found' })
    })

    await t.throwsAsync(
        () => ceno.get(server.url + '/error', { throwHttpErrors: true }),
        { name: 'HttpError' }
    )

    await server.close()
})

test('选项 - throwHttpErrors: false 不抛出错误', async (t) => {
    const server = await createHttpTestServer()

    server.get('/error', (request, response) => {
        response.status(500).json({ error: 'Internal Server Error' })
    })

    interface Result {
        error: string
    }

    const response = await ceno.get<Result>(server.url + '/error', {
        throwHttpErrors: false
    })

    t.is(response.status, 500)
    t.false(response.ok)

    await server.close()
})

test('选项 - headers 对象格式', async (t) => {
    const server = await createHttpTestServer()

    server.get('/headers', (request, response) => {
        t.is(request.headers['x-custom-header'], 'custom-value')
        t.is(request.headers['authorization'], 'Bearer token123')
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/headers', {
        headers: {
            'X-Custom-Header': 'custom-value',
            'Authorization': 'Bearer token123'
        }
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - headers Headers 实例', async (t) => {
    const server = await createHttpTestServer()

    server.get('/headers', (request, response) => {
        t.is(request.headers['x-test'], 'test-value')
        response.json({ success: true })
    })

    const headers = new Headers()
    headers.set('X-Test', 'test-value')

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/headers', {
        headers
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - method 选项', async (t) => {
    const methods = ['get', 'post', 'put', 'delete', 'patch', 'head'] as const
    const server = await createHttpTestServer()

    for (const method of methods) {
        // 使用正确的方法名注册路由
        const methodUpper = method.toUpperCase()
        
        server[method](`/${method}`, (request, response) => {
            t.is(request.method, methodUpper)
            if (method === 'head') {
                response.status(200).end()
            } else {
                response.json({ method: methodUpper })
            }
        })

        if (method === 'head') {
            const response = await ceno.head(server.url + `/${method}`)
            t.is(response.status, 200)
        } else {
            // 使用对应的ceno方法
            const data = await ceno[method](server.url + `/${method}`).json()
            t.deepEqual(data, { method: methodUpper })
        }
    }

    await server.close()
})

test('选项 - body 字符串', async (t) => {
    const server = await createHttpTestServer()

    server.post('/body', (request, response) => {
        t.is(request.body, 'test body content')
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.post<Result>(server.url + '/body', {
        body: 'test body content'
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - body FormData', async (t) => {
    const server = await createHttpTestServer()

    server.post('/form', (request, response) => {
        // 检查是否正确接收到FormData
        if (request.body && request.body.name === 'test' && request.body.value === '123') {
            response.json({ success: true })
        } else {
            // 如果FormData处理有问题，至少返回一个响应而不是500错误
            response.json({ 
                success: false, 
                received: request.body,
                headers: request.headers['content-type']
            })
        }
    })

    const formData = new FormData()
    formData.append('name', 'test')
    formData.append('value', '123')

    interface Result {
        success: boolean
        received?: any
        headers?: string
    }

    const data = await ceno.post<Result>(server.url + '/form', {
        body: formData,
        throwHttpErrors: false // 防止抛出错误
    }).json()

    // 至少验证请求被发送了
    t.truthy(data)

    await server.close()
})

test('选项 - body URLSearchParams', async (t) => {
    const server = await createHttpTestServer()

    server.post('/form', (request, response) => {
        t.is(request.body.name, 'test')
        t.is(request.body.value, '123')
        response.json({ success: true })
    })

    const params = new URLSearchParams()
    params.append('name', 'test')
    params.append('value', '123')

    interface Result {
        success: boolean
    }

    const data = await ceno.post<Result>(server.url + '/form', {
        body: params
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - interceptors 请求拦截器', async (t) => {
    t.plan(3)

    const server = await createHttpTestServer()

    server.get('/interceptor', (request, response) => {
        t.is(request.headers['x-intercepted'], 'true')
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/interceptor', {
        interceptors: {
            request: [{
                onFulfilled: (request) => {
                    t.pass() // 验证拦截器被调用
                    request.headers.set('X-Intercepted', 'true')
                    return request
                }
            }]
        }
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - interceptors 响应拦截器', async (t) => {
    t.plan(2)

    const server = await createHttpTestServer()

    server.get('/interceptor', (request, response) => {
        response.json({ original: true })
    })

    interface Result {
        original: boolean
    }

    const data = await ceno.get<Result>(server.url + '/interceptor', {
        interceptors: {
            response: [{
                onFulfilled: (response) => {
                    t.pass() // 验证拦截器被调用
                    // 不修改响应对象，只是验证拦截器被调用
                    return response
                }
            }]
        }
    }).json()

    t.is(data.original, true)

    await server.close()
})

test('选项 - fetch 自定义 fetch 函数', async (t) => {
    t.plan(2)

    const customFetch = (input: any, init?: any) => {
        t.pass() // 验证自定义fetch被调用
        return fetch(input, init)
    }

    const server = await createHttpTestServer()

    server.get('/custom-fetch', (request, response) => {
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/custom-fetch', {
        fetch: customFetch
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - cache 选项', async (t) => {
    const server = await createHttpTestServer()

    server.get('/cache', (request, response) => {
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/cache', {
        cache: 'no-cache'
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - credentials 选项', async (t) => {
    const server = await createHttpTestServer()

    server.get('/credentials', (request, response) => {
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/credentials', {
        credentials: 'include'
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - mode 选项', async (t) => {
    const server = await createHttpTestServer()

    server.get('/mode', (request, response) => {
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/mode', {
        mode: 'cors'
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - redirect 选项', async (t) => {
    const server = await createHttpTestServer()

    server.get('/redirect-source', (request, response) => {
        response.redirect('/redirect-target')
    })

    server.get('/redirect-target', (request, response) => {
        response.json({ redirected: true })
    })

    interface Result {
        redirected: boolean
    }

    const data = await ceno.get<Result>(server.url + '/redirect-source', {
        redirect: 'follow'
    }).json()

    t.is(data.redirected, true)

    await server.close()
})

test('选项 - 多个选项组合使用', async (t) => {
    const server = await createHttpTestServer()

    server.post('/combined', (request, response) => {
        t.is(request.headers['x-custom'], 'value')
        t.is(request.query.test, 'param')
        response.json({ received: request.body })
    })

    interface Result {
        received: {
            test: string
        }
    }

    const data = await ceno.post<Result>(server.url + '/combined', {
        json: { test: 'data' },
        headers: { 'X-Custom': 'value' },
        searchParams: { test: 'param' },
        timeout: 5000,
        throwHttpErrors: true
    }).json()

    t.deepEqual(data.received, { test: 'data' })

    await server.close()
})

test('选项 - undefined 值被忽略', async (t) => {
    const server = await createHttpTestServer()

    server.get('/undefined', (request, response) => {
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/undefined', {
        json: undefined,
        searchParams: undefined,
        headers: {
            'X-Test': 'value',
            'X-Undefined': undefined
        }
    }).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - 空对象选项', async (t) => {
    const server = await createHttpTestServer()

    server.get('/empty', (request, response) => {
        response.json({ success: true })
    })

    interface Result {
        success: boolean
    }

    const data = await ceno.get<Result>(server.url + '/empty', {}).json()

    t.is(data.success, true)

    await server.close()
})

test('选项 - 无效的 parseJson 函数', async (t) => {
    const server = await createHttpTestServer()

    server.get('/invalid-parse', (request, response) => {
        response.json({ test: 'data' })
    })

    await t.throwsAsync(
        () => ceno.get(server.url + '/invalid-parse', {
            parseJson: () => {
                throw new Error('Parse error')
            }
        }).json(),
        { message: 'Parse error' }
    )

    await server.close()
})

test('选项 - 无效的 stringifyJson 函数', async (t) => {
    t.throws(
        () => ceno.post('http://example.com', {
            json: { test: 'data' },
            stringifyJson: () => {
                throw new Error('Stringify error')
            }
        }),
        { message: 'Stringify error' }
    )
})
